Pywinauto 自動控制windows

入門

Pywinauto可以控制Windows,它支持多種Windows控制,基本上可被呼叫的或可選的項目,都能找到對應的句炳並進行操作,非常好用。

Windows自動控制還有一個工具Autoit,這非常好入門,但缺點是支持的控制項並沒有pywinauto多。

官方說明文檔

測試環境

系統

Windows 7 sp1

python

  1. python 2.7

    檔案下載

  2. pywin32

    檔案下載

  3. pywinauto

    1
    pip install pywinauto
  4. (可選)pyinstaller

    打包成exe時使用

    1
    pip install pyinstaller
  • 以上模塊安裝完,所產生的相關依賴版本

這邊紀錄是因為之前環境有安裝過相同依賴,但版本不同導致無法打包。

1
2
3
4
5
6
7
8
9
10
altgraph==0.15
comtypes==1.1.4
dis3==0.1.2
future==0.16.0
macholib==1.9
pefile==2017.11.5
PyInstaller==3.3.1
pywin32==221
pywinauto==0.6.3
six==1.11.0

輔助工具

  1. sky+

sky+工具能將選擇的句柄操作,生成pywinauto代碼,直接貼上就可以用了,

下載 SWAPY

  1. Autoit Windows info

安裝Atoit,這裡頭的小工具autoitinfo可以快速查找窗口句炳。

AutoIt Full Installation

邊做邊學

連接句柄

  • 導入模塊
1
from pywinauto.application import Application
  • 連接程序

方式一:創建pywinauto對象,開啟並連接notepad.exe程序句柄

1
2
3
4
app = Application().start("notepad.exe")
>>> app
<pywinauto.application.Application object at 0x03106510>
# 輸入app可以看到pywinauto對象已經建立

方式二:綁定啟動過程序內的新句柄

這個方法是使用pywinauto對象之下,主要是用在連接另外新開啟的dig,如:說明、開啟/另存檔案等,因為這些不能算是一個主程序。

1
about_dlg = app.window_(title_re = u"另存为")

方式三:綁定已開啟的程序句柄

1
app = Application().connect(title_re = "", class_name = "")

參數title、class_name可以透過輔助工具查看到

控制

控制心法

程序控制

如果class有中文或字符可以在[]中輸入class_name

1
2
3
app."class"."controls"
or
app["class"]."controls"

程序內控制項操作

1
app."class"."class"."controls"
  • 控制目前app下的句柄程序

雖然app只是一個pywinauto的對象,如果要操作還是需要輸入底下connect的class才可以操作。

“class” 為透過pywinauto所開啟的class,這必須是connect過的class否則無法直接進行控制,這部份是需要注意的。

“操作” 為該程序可以控制的所有方式,具體調用方法可以參考官方文檔,由下圖可以看到官方文檔其實已經針對了不同需求,提供了方法。

  • 查看app.class內有什麼控制項
1
app."class".print_control_identifiers()

這邊以notepad為例進行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> app.notepad.print_control_identifiers()
Control Identifiers:

Notepad - '无标题 - 记事本' (L139, T83, R832, B572)
[u'\u65e0\u6807\u9898 - \u8bb0\u4e8b\u672cNotepad', u'\u65e0\u6807\u9898 - \u8bb
0\u4e8b\u672c', u'Notepad']
child_window(title="无标题 - 记事本", class_name="Notepad")
|
| Edit - '' (L147, T133, R824, B564)
| [u'Edit', u'\u65e0\u6807\u9898 - \u8bb0\u4e8b\u672cEdit']
| child_window(class_name="Edit")
|
| StatusBar - '' (L147, T542, R824, B564)
| ['StatusBar', u'\u65e0\u6807\u9898 - \u8bb0\u4e8b\u672cStatusBar', u'Status
Bar \u7b2c 1 \u884c\uff0c\u7b2c 1 \u5217']
| child_window(class_name="msctls_statusbar32")

看到Notepad下面有Edit、StatusBar可以控制,要了解控制項的方法就必須得看文檔。

win32_controls

Control Identifiers

這邊是自己操作一些控制項的紀錄

Edit

win32_controls.EditWrapper

  • TypeKeys() 輸入文字
    app.notepad.Edit.TypeKeys(“1\thello,hello\rwahaha”)

  • SetEditText() 替換文字
    app.notepad.Edit.SetEditText(“全部都被我換掉…”)

pywinauto.controls.menuwrapper

  • Menu().ItemCount() 計算選單數量
1
2
>>> app.notepad.Menu().ItemCount()
5
  • Menu().Item(“int”).Text() 查看第”int”選單的文字,由0開始為第1個。
1
2
3
4
5
6
7
8
9
10
>>> print app.notepad.Menu().Item(0).Text()
文件(&F)
>>> print app.notepad.Menu().Item(1).Text()
编辑(&E)
>>> print app.notepad.Menu().Item(2).Text()
格式(&O)
>>> print app.notepad.Menu().Item(3).Text()
查看(&V)
>>> print app.notepad.Menu().Item(4).Text()
帮助(&H)

控制子選單 SubMenu()

先選擇上層選單,在透過SubMenu()方法控制子選單。

  • 方式一:

類似root層級,透過固定的位置逐層選擇,該方法也適用於遍歷尋找指定選單位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 上層選單 "文件(&F)" - Menu().Item(0)

# 計算數量 - ItemCount()
>>> app.notepad.Menu().Item(0).SubMenu().ItemCount()
9

# 遍歷子選單名稱 - Text()

>>> SubMenuCount1 = app.notepad.Menu().Item(0).SubMenu().ItemCount()
>>>
>>> for x in range(SubMenuCount1):
... print app.notepad.Menu().Item(0).SubMenu().Item(x).Text()
...
新建(&N) Ctrl+N
打开(&O)... Ctrl+O
保存(&S) Ctrl+S
另存为(&A)...

页面设置(&U)...
打印(&P)... Ctrl+P

退出(&X)

選擇”另存为(&A)…” - ClickInput()

1
2
3
4
5
6
7
8
9

>>> SubMenuCount1 = app.notepad.Menu().Item(0).SubMenu().ItemCount()
>>>
>>> for x in range(SubMenuCount1):
... sub = app.notepad.Menu().Item(0).SubMenu().Item(x)
... subname = sub.Text()
... if u"另存为(&A)..." in subname:
... sub.ClickInput()
...
  • 方式二

直接選擇已知”選單名”

1
2
# 方式一的部分這邊一行就可以搞定了。
app.Notepad.MenuSelect(u"文件(&F)->另存为(&A)...")

對話框 dialog

windows中對話框的種類非常多,可以參考下面的連結了解一下。

https://www.blog.pythonlibrary.org/2010/06/26/the-dialogs-of-wxpython-part-1-of-2/
https://www.blog.pythonlibrary.org/2010/07/10/the-dialogs-of-wxpython-part-2-of-2/

連接對話框

1
about_dlg = app.window_(title_re = u"另存为")

由於對話框是一個新的窗口,必須先連接後在用print_control_identifiers查看有哪些win32_controls。

1
about_dlg.print_control_identifiers()

SysTreeView32

controls.common_controls.TreeViewWrapper

官方文檔中對於每一個物件都做了說明,建議操作時同時參考查閱。

  • ItemCount() 列出所有個數
1
2
>>> about_dlg.SysTreeView32.ItemCount()
18
  • PrintItems() 打印所有項目名稱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> print about_dlg.SysTreeView32.PrintItems()
樹狀檢視
我的最愛
下載
桌面
最近的位置
桌面
媒體櫃
文件
音樂
視訊
圖片
家用群組
電腦
本機磁碟 (C:)
DVD 光碟機 (D:) Parallels Tools
Home on 'Mac' (Z:)
網路

點擊

  • 我的最愛\下載
1
2
>>> about_dlg.SysTreeView32.GetItem(u"\我的最愛\下載").ClickInput()
<pywinauto.controls.common_controls._treeview_element object at 0x0317E7F0>
  • 桌面\電腦\本機磁碟 (C:)
1
2
>>> about_dlg.SysTreeView32.Item(u"\桌面\電腦\本機磁碟 (C:)").ClickInput()
<pywinauto.controls.common_controls._treeview_element object at 0x0317E530>

先展開,再點擊

指定展開 “桌面\電腦\本機磁碟 (C:)”

1
2
3
>>> about_dlg.SysTreeView32.GetItem(u"\桌面\電腦\本機磁碟 (C:)").Click(where =
"button")
<pywinauto.controls.common_controls._treeview_element object at 0x03191370>

打印所有項目名稱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> print about_dlg.SysTreeView32.PrintItems()
樹狀檢視
我的最愛
下載
桌面
最近的位置
桌面
媒體櫃
文件
音樂
視訊
圖片
家用群組
電腦
本機磁碟 (C:)
BaiduNetdiskDownload
PerfLogs
Program Files
Program Files (x86)
Python27
Windows
使用者
DVD 光碟機 (D:) Parallels Tools
Home on 'Mac' (Z:)
網路

點擊 剛剛展開”本機磁碟 (C:)”中的Python27

1
2
3
>>> about_dlg.SysTreeView32.Item(u"\桌面\電腦\本機磁碟 (C:)\Python27").ClickInput
()
<pywinauto.controls.common_controls._treeview_element object at 0x031993B0>

example

使用心得 - 操作記事本

整理上述實例,開始到結束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -*- coding: utf8 -*-
from pywinauto.application import Application

# 開啟並連接程序
app = Application().start("notepad.exe")

# 控制選單
app.Notepad.MenuSelect(u"文件(&F)->另存为(&A)...")

# 連接子句柄
about_dlg = app.window_(title_re = u"另存为")
# print about_dlg.print_control_identifiers()

# 打印SysTreeView32控制項
about_dlg.SysTreeView32.print_control_identifiers()

# 點擊左側 樹狀選單 "我的最愛\下載"
about_dlg.SysTreeView32.GetItem(u"\我的最愛\下載").ClickInput()

# 展開左側 本地磁盘 (C:) 樹狀選單
about_dlg.SysTreeView32.GetItem(u"\桌面\電腦\本機磁碟 (C:)").Click(where =
"button")

# 打印樹狀選單所有的項目
print about_dlg.SysTreeView32.PrintItems()

# 點擊左側 樹狀選單 "Intel"
about_dlg.SysTreeView32.Item(u"\桌面\電腦\本機磁碟 (C:)\Python27").ClickInput
()

小結

盡可能參考官方文檔查詢控制方法,找class名稱或底下控制項,可以使用autoitinfo、spy+取得名稱,開發過程使用交互模式調試非常方便。

參考